package  
{
	import flash.utils.ByteArray;
	/**
	 * 可变长度字节读写工具
	 * 参考SWFWireDecompiler(https://github.com/magicalhobo/SWFWire)
	 * @author WLDragon
	 */
	public class ByteUtil 
	{
		static private const FILTER7:uint = ~0 >>> -7;//表示二进制7个1
		static private const FILTER30:uint = ~0 >>> -30;//表示二进制30个1
		
		static private var bitPosition:uint = 0;
		
		static public function alignByte(bytes:ByteArray):void
		{
			if (bitPosition > 0) {
				bitPosition = 0;
				bytes.position++;
			}
		}
		
		static public function readUB(length:uint, bytes:ByteArray):uint
		{
			if(!length) return 0;
			
			var totalBytes:uint = Math.ceil((bitPosition + length) / 8);
			
			var iter:uint = 0;
			var currentByte:uint = 0;
			var result:uint = 0;
			
			while(iter < totalBytes) {
				currentByte = bytes.readUnsignedByte();
				result = (result << 8) | currentByte;
				iter++;
			}
			
			var newBitPosition:uint = ((bitPosition + length) % 8);
			
			var excessBits:uint = (totalBytes * 8 - (bitPosition + length));
			result = result >> excessBits;
			result = result & (~0 >>> -length);
			
			bitPosition = newBitPosition;
			if(bitPosition > 0) bytes.position--;
			return result;
		}
		
		static public function readSB(length:uint, bytes:ByteArray):int
		{
			if(!length) return 0;
			
			var result:int = readUB(length, bytes);
			var leadingDigit:uint = result >>> (length - 1);
			if(leadingDigit == 1) {
				return -((~result & (~0 >>> -length)) + 1);
			}
			return result;
		}
		
		static public function readU30(bytes:ByteArray):uint
		{
			return readU32(bytes) & FILTER30;
		}
		
		static public function readS32(bytes:ByteArray):int
		{
			return int(readU32(bytes));
		}
		
		static public function readU32(bytes:ByteArray):uint
		{
			var result:uint;
			var bytesRead:uint;
			var currentByte:uint;
			var shouldContinue:Boolean = true;
			while(shouldContinue && bytesRead < 5) {
				currentByte = bytes.readUnsignedByte();
				result = ((currentByte & FILTER7) << (7 * bytesRead)) | result;
				shouldContinue = ((currentByte >> 7) == 1);
				bytesRead++;
			}
			return result;
		}
		
		static public function writeU30(value:uint, bytes:ByteArray):void
		{
			var remaining:uint = value & FILTER30;
			var bytesWritten:uint;
			var currentByte:uint;
			var shouldContinue:Boolean = true;
			while(shouldContinue && bytesWritten < 5) {
				currentByte = remaining & FILTER7;
				remaining = remaining >> 7;
				if(remaining > 0) {
					currentByte = currentByte | (1 << 7);
				}
				bytes.writeByte(currentByte);
				shouldContinue = remaining > 0;
				bytesWritten++;
			}
		}
		
		static public function readString(bytes:ByteArray):String
		{
			var byteCount:uint = 1;
			while(bytes.readUnsignedByte()) {
				byteCount++;
			}
			bytes.position -= byteCount;
			var result:String = bytes.readUTFBytes(byteCount);
			return result;
		}
	}

}